package com.gitee.wsl.compose.chart.geometry.model.pool

/*
 * Copyright 2013 Hannes Janetzek
 *
 * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
 *
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Utility class for making poolable objects.
 * Instead of using an additional list to hold pool items just extend this
 * class.
 *
 * Also handy for objects that exist in only *one list* at a time, if you
 * are *REALLY* sure about it. Better do not use it! :)
 */
open class Inlist<T : Inlist<T>> {
    class List<T: Inlist<T>> : Iterable<T>, MutableIterator<T> {
        private var head: T? = null
        private var cur: T? = null

        /**
         * Insert single item at start of list.
         * item.next must be null.
         */
        fun push(it: T) {
            require(it.next == null) { "item.next must be null" }
            (it as Inlist<T>).next = head
            head = it
        }

        /**
         * Insert item at start of list.
         */
        fun pop(): T? {
            if (head == null) return null
            val it: Inlist<T> = head!!
            head = it.next
            it.next = null
            return it as T
        }

        /**
         * Reverse list.
         */
        fun reverse() {
            var tmp: T?
            var itr = head
            head = null
            while (itr != null) {
                /* keep next */
                tmp = itr.next

                /* push itr onto new list */
                itr.next = head
                head = itr
                itr = tmp
            }
        }

        /**
         * Append item, O(n) - use push() and
         * reverse() to iterate in insertion order!
         */
        fun append(it: T) {
            head = appendItem(head, it)
        }

        /**
         * Append Inlist.
         */
        fun appendList(list: T) {
            head = appendList(head, list)
        }

        /**
         * Remove item from list.
         */
        fun remove(node: T) {
            cur = null
            head?.let {
                head = remove(it, node)
            }
        }

        /**
         * Clear list.
         *
         * @return head of list
         */
        fun clear(): T? {
            val ret = head
            head = null
            cur = null
            return ret as T?
        }

        /** @return first node in list
         */
        fun head(): T? {
            return head as T?
        }

        /** Iterator: Has next item  */
        override fun hasNext(): Boolean {
            return cur != null
        }

        /** Iterator: Get next item  */
        override fun next(): T {
            checkNotNull(cur)
            val tmp = cur
            cur = cur!!.next
            return tmp as T
        }

        /** Iterator: Remove current item  */
        override fun remove() {
            /* iterator is at first position */
            if (head!!.next === cur) {
                head = head!!.next
                return
            }
            var prev = head
            while (prev!!.next!!.next !== cur) prev = prev!!.next
            prev!!.next = cur
        }

        /** NB: Only one iterator at a time possible!  */
        override fun iterator(): Iterator<T> {
            cur = head
            return this
        }

        fun size(): Int {
            return size(head)
        }
    }

    var next: T? = null

    operator fun next(): T? {
        return next
    }

    companion object {
        /**
         * Push 'item' onto 'list'.
         *
         * @param list the list
         * @param item the item
         * @return the new head of 'list' (item)
         */

        fun <T : Inlist<T>> push(list: T, item: T): T {
            require(item.next == null) { "'item' is a list" }
            (item as Inlist<T>).next = list
            return item
        }

        /**
         * Get size of 'list'.
         *
         * @param list the list
         * @return the number of items in 'list'
         */
        fun <T : Inlist<T>> size(list: T?): Int {
            var count = 0
            var l: Inlist<*>? = list
            while (l != null) {
                count++
                l = l.next
            }
            return count
        }

        /**
         * Removes the 'item' from 'list'.
         *
         * @param list the list
         * @param item the item
         * @return the new head of 'list'
         */

        fun <T : Inlist<T>> remove(list: T, item: T): T {
            if (item === list) {
                val head = item.next!!
                item.next = null
                return head
            }
            var prev: Inlist<T>? = list
            var it:Inlist<T>? = list.next
            while (it != null) {
                if (it === item) {
                    prev!!.next = item.next
                    item.next = null
                    return list
                }
                prev = it
                it = it.next
            }
            return list
        }

        /**
         * Gets the 'item' with index 'i'.
         *
         * @param list the list
         * @param i the index
         * @return the item or null
         */

        operator fun <T : Inlist<T>> get(list: T, i: Int): T? {
            var list: T? = list
            var i = i
            if (i < 0) return null
            while (--i > 0 && list != null) list = list.next as T
            return if (i == 0) list else null
        }

        /**
         * Append 'item' to 'list'. 'item' may not be in another list,
         * i.e. item.next must be null
         *
         * @param list the list
         * @param item the item
         * @return the new head of 'list'
         */

        fun <T : Inlist<T>> appendItem(list: T?, item: T): T {
            require(item.next == null) { "'item' is list" }
            if (list == null) return item
            var it: Inlist<T> = list
            while (it.next != null) it = it.next!!
            it.next = item
            return list
        }

        /**
         * Append list 'other' to 'list'.
         *
         * @param list the list
         * @param other the other
         * @return the head of 'list'
         */

        fun <T : Inlist<T>> appendList(list: T?, other: T?): T? {
            if (list == null) return other
            if (other == null) return list
            var it: Inlist<T> = list
            while (true) {
                if (it.next == null) {
                    it.next = other
                    break
                }
                it = it.next!!
            }
            return list
        }

        /**
         * Get last item in from list.
         *
         * @param list the list
         * @return the last item
         */

        fun <T : Inlist<T>> last(list: T): T? {
            var list: T? = list
            while (list != null) {
                if (list.next == null) return list
                list = list.next as T
            }
            return null
        }

        /**
         * Prepend 'item' relative to 'other'.
         *
         * @param list the list
         * @param item the item
         * @param other the other list
         * @return the new head of list
         */

        fun <T : Inlist<T>> prependRelative(list: T?, item: T, other: T): T {
            require(item.next == null) { "'item' is list" }
            requireNotNull(list) { "'list' is null" }
            if (list === other) {
                (item as Inlist<T>).next = list
                return item
            }
            var it = list
            while (it != null && it.next !== other) it = it.next as T
            requireNotNull(it) { "'other' not in 'list'" }
            (item as Inlist<T>).next = it.next
            (it as Inlist<T>).next = item
            return list
        }
    }
}

